﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DiscreteEventModel.Common {

    public class GarbageCollector<T> {

        public interface IContext {
            IEnumerable<T> GetObjects();
            IEnumerable<T> GetObjects(T obj);
            void CollectObject(T obj);
        }

        private IContext _context;
        private HashSet<T> _objects;

        public GarbageCollector(IContext context) {
            this._context = context;
            this._objects = new HashSet<T>();
            foreach(T rootObj in this._context.GetObjects()) {
                this.Trace(rootObj, this._objects);
            }
        }

        private void Trace(T obj, HashSet<T> liveObjects) {
            lock(this._objects) {
                this._objects.Remove(obj);
            }

            liveObjects.Add(obj);

            foreach(T subObj in this._context.GetObjects(obj)) {
                this.Trace(subObj, liveObjects);
            }
        }

        public void Collect() {
            HashSet<T> liveObjects = new HashSet<T>();

            foreach(T rootObj in this._context.GetObjects()) {
                this.Trace(rootObj, liveObjects);
            }

            foreach(T deadObj in this._objects) {
                this._context.CollectObject(deadObj);
            }

            this._objects = liveObjects;
        }

        public IEnumerable<T> GetLiveObjects() {
            lock(this._objects) {
                foreach(T obj in this._objects) {
                    yield return obj;
                }
            }
        }

    }
}
